#include "StdAfx.h"
#include "VTreeModel.h"

// VTreeModel /////////////////////////////////////////////////////////////////

//
VTreeModel::VTreeModel(QObject *parent)
	:QAbstractItemModel(parent)
	,m_rootNode(NULL)
{
	m_rootNode = new VTreeNode;
	VItemFolder *folder = new VItemFolder;
		folder->setIdNavParent(-1);
		folder->setIdNav(0);
	m_rootNode->setItem(folder);
	m_rootNode->setHasChildren(true);
	
	reset();
}

//
VTreeModel::~VTreeModel()
{
	delete m_rootNode;
}

//
void VTreeModel::setRootNode(VTreeNode *node)
{
	delete m_rootNode;
	m_rootNode = node;

	reset();
}

//
QModelIndex VTreeModel::index(int row, int column,const QModelIndex &parent) const
{
	//if (parent.isValid() && parent.column() != 0)
	//	return QModelIndex();

	VTreeNode *parentNode = nodeFromIndex(parent);
	if (!parentNode)
		return QModelIndex();

	if (row >= parentNode->childCount())
		return QModelIndex();

	VTreeNode *childNode = parentNode->child(row);
	if (childNode)
		return createIndex(row, column, childNode);

	return QModelIndex();
}

//
QModelIndex VTreeModel::indexFromNode(VTreeNode *node) const
{
	//VTreeNode *parentNode = node->parent();
	//if (!parentNode)
	//	return QModelIndex();

	return createIndex(node->row(),0,node);
}

//
VTreeNode *VTreeModel::nodeFromIndex(const QModelIndex &index) const
{
	if (index.isValid()) 
	{
		VTreeNode *node = static_cast<VTreeNode*>(index.internalPointer());
		if (node) 
			return node;
	}
	return m_rootNode;
}

//
bool VTreeModel::canFetchMore(const QModelIndex &parent) const
{
	//if (hasChildren(parent))
	//	return false;
		
	VTreeNode *parentNode = nodeFromIndex(parent);
	if (!parentNode)
		return false;

	return parentNode->hasChildren() && !parentNode->childCount();

	//VSqlDatabase db;
	//return VTreeSqlHasChildren(db).exec(parentNode->item());
}

//
void VTreeModel::fetchMore(const QModelIndex &parent)
{
	VTreeNode *parentNode = nodeFromIndex(parent);
	if (!parentNode)
		return;

	VSqlDatabase db;
	QList<VTreeNode*> children;
	VTreeSqlSelectChildrenNode(db).exec(parentNode,children);

	beginInsertRows(parent, 0, children.count()-1);
		parentNode->setChildren(children);
	endInsertRows();
}

//
bool VTreeModel::hasChildren(const QModelIndex &parent) const
{
	VTreeNode *parentNode = nodeFromIndex(parent);
	if (!parentNode)
		return false;

	return parentNode->hasChildren();
}

//
QModelIndex VTreeModel::parent(const QModelIndex &index) const
{
	if (!index.isValid())
		return QModelIndex();

	VTreeNode *childNode = nodeFromIndex(index);
	if (!childNode)
		return QModelIndex();
	
	VTreeNode *parentNode = childNode->parent();

	if (parentNode == m_rootNode)
		return QModelIndex();

	return createIndex(parentNode->row(), 0, parentNode);
}

//
int VTreeModel::rowCount(const QModelIndex &parent) const
{
	VTreeNode *parentNode = nodeFromIndex(parent);
	if (!parentNode)
		return 0;

	return parentNode->childCount();
}

//
int VTreeModel::columnCount(const QModelIndex &parent) const
{
	VTreeNode *parentNode = nodeFromIndex(parent);
	if (!parentNode)
		return 0;

	return VProperty(parentNode->item()).count()+10;
}

//
Qt::ItemFlags VTreeModel::flags(const QModelIndex &index) const
{
	if (!index.isValid())
		return Qt::ItemIsEnabled | Qt::ItemIsDropEnabled;

	Qt::ItemFlags f 
		= Qt::ItemIsEditable 
		| Qt::ItemIsEnabled 
		| Qt::ItemIsSelectable;

	if (index.column() == 0)
		f |= Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
		
	return f;
}

//
Qt::DropActions VTreeModel::supportedDropActions() const
{
	return /*Qt::CopyAction |*/ Qt::MoveAction;
}

//
QStringList VTreeModel::mimeTypes() const
{
	QStringList types;
		types << "application/vidar.VMetaModel.idNav";
		
	return types;
}

//
QMimeData *VTreeModel::mimeData(const QModelIndexList &indexes) const
{
	QByteArray encodedData;
	QDataStream stream(&encodedData, QIODevice::WriteOnly);

	foreach (QModelIndex index, indexes) 
	{
		if (index.isValid()) 
		{
			VTreeNode *node = nodeFromIndex(index);
			if (!node)
				return NULL;

			stream << node->item()->idNav();
		}
	}

	QMimeData *mimeData = new QMimeData();
		mimeData->setData("application/vidar.VMetaModel.idNav", encodedData);
		
	return mimeData;
}

//
bool VTreeModel::dropMimeData
(
	const QMimeData *data,Qt::DropAction action, 
	int row, int column, const QModelIndex &parentIndex
)
{
	if (action == Qt::IgnoreAction)
		return true;

	if (!data->hasFormat("application/vidar.VMetaModel.idNav"))
		return false;

	if (column > 0)// || !parentIndex.isValid())
		return false;

	QByteArray encodedData = data->data("application/vidar.VMetaModel.idNav");
	QDataStream stream(&encodedData, QIODevice::ReadOnly);
	VIntegerList dropList;
	int rows = 0;

	while (!stream.atEnd()) 
	{
		int idNav;
		stream >> idNav;
		dropList << idNav;
		++rows;
	}

	int currRow;
	QModelIndex currIndex;
	VTreeSqlMoveNode::BAC bac;

	if (row != -1)
	{
		currIndex = parentIndex;
		currRow = row;
		bac = VTreeSqlMoveNode::before;

		if (row >= rowCount(currIndex))
		{
			currRow--;
			bac = VTreeSqlMoveNode::after;
		}
	}
	else if (parentIndex.isValid())
	{
		currIndex = parentIndex.parent();
		currRow = parentIndex.row();
		bac = VTreeSqlMoveNode::child;
	}
	else
	{
		return false;
		//currRow = rowCount(QModelIndex());
		//currIndex = QModelIndex();
	}

	//insertRows(beginRow, rows, QModelIndex());

	if (action == Qt::MoveAction)
	{
		action = action;
	}

	VSqlDatabase db;
	db.transaction();
	VTreeSqlMoveNode sqlMoveNode(db);
	bool isOk = false;
	
	foreach (int idNav,dropList) 
	{
		VTreeNode *dropNode = m_rootNode->findNodeByIdNav(idNav);
		if (!dropNode)
			continue;
			
		QModelIndex dropIndex = indexFromNode(dropNode);
//VTreeNode *nn = nodeFromIndex(dropIndex.parent());
		QModelIndex rowIndex = index(currRow, 0, currIndex);
		VTreeNode *currNode = nodeFromIndex(rowIndex);

		isOk = beginMoveRows(dropIndex.parent(),dropIndex.row(),dropIndex.row(), rowIndex.parent(),rowIndex.row());
		if (!isOk)
			break;
		
		if (bac == VTreeSqlMoveNode::child)
			currNode->moveNode(dropNode,currRow);
		else
			currNode->parent()->moveNode(dropNode,currRow);
		endMoveRows();

		currRow++;

		isOk = sqlMoveNode.exec(dropNode->item(),currNode->item(),bac);
		if (!isOk)
			break;
	}

	if (isOk)
		db.commit();
	else
		db.rollback();
		
	return isOk;
}

//
QVariant VTreeModel::headerData(int section,Qt::Orientation orientation,int role) const
{
	if (orientation == Qt::Horizontal)
	{
		VProperty prop(m_rootNode->item());
		
		QString propName = section < prop.count() 
			? prop.name(section) 
			: QString("Unknown");
		
		switch (role)
		{
			case Qt::DisplayRole:
				return propName;
				
			case Qt::TextAlignmentRole:
				/*
				if (
					propName == VItem::propIdName() ||
					propName == VItem::propIdNav() ||
					propName == VItem::propIdNavParent()
				)
					return int(Qt::AlignRight	|	Qt::AlignVCenter);
				else
				*/
					return int(Qt::AlignHCenter	|	Qt::AlignVCenter);
		}
	}
		
	return QVariant();
}

//
QVariant VTreeModel::data(const QModelIndex &index, int role) const
{
	if (!index.isValid())
		return QVariant();

	int column = index.column();
	VTreeNode *node = nodeFromIndex(index);
	VProperty prop(node->item());

	if (column < 0 || column >= prop.count())
	{
		if (role == Qt::DisplayRole)
			return QString("");
		
		return QVariant();
	}

	switch (role)
	{
		case Qt::TextAlignmentRole:
		{
			QVariant value = prop.value(column);
			QMetaProperty mp = prop.metaProperty(column);
			if (!mp.isEnumType() && (value.type() == QVariant::Int))
				return int(Qt::AlignRight	|	Qt::AlignVCenter);
		}
		break;
		
		case Qt::DisplayRole:
		{
			QVariant value = prop.value(column);
			
			QMetaProperty mp = prop.metaProperty(column);
			if (mp.isEnumType())
				return VPropertyEnum(mp).display(value);

			if (value.isNull())
				return QString("");
			
			return value;
		}
		break;

		case Qt::EditRole:
		{
			QVariant value = prop.value(column);

			if (value.isNull())
				return QString("");

			return value;
		}
		break;

		case Qt::DecorationRole:
		{
			if (column == 0)
				return node->hasChildren() ? VIcons::folder() : VIcons::item();
		}
		break;

		case Qt::TextColorRole:	return VItemStyle::style(node->item(),column).color;
		case Qt::FontRole:			return VItemStyle::style(node->item(),column).font;
	}

	return QVariant();
}

//
bool VTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
	if (role != Qt::EditRole)
		return false;

	VTreeNode *node = nodeFromIndex(index);

	VTreeSqlMaps::VDbStruct dbStruct;
	if (!VTreeSqlMaps::dbStruct(node->item(),index.column(),dbStruct))
		return false;
	if (!dbStruct.enEdit)
		return false;

	bool result = false;

	VProperty prop(node->item());
	QString propName = prop.name(index.column());
	
	if (propName == VItem::propItemType())
	{
		if (data(index,role) != value)
		{
			VItem *newItem = VItem::itemFactory( (VItem::EItemType)value.toInt() );
			newItem->copyFromItem(*node->item());
			node->setItem(newItem);
			result = true;
		}
		else
			result = false;
	}
	else
		result = VProperty(node->item()).setValue(index.column(), value);

	if (result)
	{
		VSqlDatabase db;
		db.transaction();

		if (VTreeSqlUpdateNode(db).exec(node->item(),index.column()))
			db.commit();
		else
			db.rollback();
	}

	if (result)
		emit dataChanged(index, index);

	return result;
}

//
bool VTreeModel::insertRows(int position,int rows,const QModelIndex &parentIndex)
{
	VTreeNode *parentNode = nodeFromIndex(parentIndex);
	if (!parentNode)
		return false;

	beginInsertRows(parentIndex, position, position + rows - 1);
	bool isOk = false;
	VSqlDatabase db;
	db.transaction();

		VTreeNode *childNode = new VTreeNode;
		VItem *item = new VItem;
		childNode->setItem(item);

		VTreeSqlInsertName(db).exec(item);
		
		if (position >= parentNode->childCount())
			isOk = VTreeSqlInsertNode(db).exec(parentNode->item(),item);
		else
		{
			QModelIndex posIndex = index(position,0, parentIndex);
			VTreeNode *posNode = nodeFromIndex(posIndex);
			isOk = VTreeSqlInsertNodeBA(db).exec(
				posNode->item(),item,VTreeSqlInsertNodeBA::before
			);
		}

		parentNode->insertChild(position,childNode);

	if (isOk) 
		db.commit();
	else
		db.rollback();
		
	endInsertRows();

	return true;
}

//
bool VTreeModel::removeRows(int position,int rows,const QModelIndex &parentIndex)
{
	VTreeNode *parentNode = nodeFromIndex(parentIndex);
	if (!parentNode)
		return false;

	beginRemoveRows(parentIndex, position, position + rows - 1);
	bool isOk = false;
	VSqlDatabase db;
	db.transaction();
	
		QModelIndex posIndex = index(position,0, parentIndex);
		VTreeNode *posNode = nodeFromIndex(posIndex);
		isOk = VTreeSqlDeleteNode(db).exec(posNode->item());
		parentNode->deleteChild(position);

	if (isOk) 
		db.commit();
	else
		db.rollback();

	endRemoveRows();

	return true;
}
